/*
 * @Author: Snow
 * @Date: 2021-09-23 19:41:37
 * @Description: 三维向量工具类
 */
export class Vec3 {
    public static add(a:number, b:number):number[] {
        let out:number[] = [];
        out[0] = a[0] + b[0];
        out[1] = a[1] + b[1];
        out[2] = a[2] + b[2];
        return out;
    }
    /**
     * Subtracts vector b from vector a
     *
     * @param {vec3} out the receiving vector
     * @param {vec3} a the first operand
     * @param {vec3} b the second operand
     * @returns {vec3} out
     */
    public static subtract(a:number[], b:number[]):number[] {
        let out:number[] = [];
        out[0] = a[0] - b[0];
        out[1] = a[1] - b[1];
        out[2] = a[2] - b[2];
        return out;
    }
    /**
     * @Author: Snow
     * @description: 叉乘
     *  相对于点乘，叉乘可能更有用吧。2维空间中的叉乘是：
      V1(x1, y1) X V2(x2, y2) = x1y2 – y1x2
      看起来像个标量，事实上叉乘的结果是个向量，方向在z轴上。上述结果是它的模。在二维空间里，让我们暂时忽略它的方向，将结果看成一个向量，那么这个结果类似于上述的点积，我们有：
      A x B = |A||B|Sin(θ)
      然而角度 θ和上面点乘的角度有一点点不同，他是有正负的，是指从A到B的角度。因此 ，向量的外积不遵守乘法交换率，因为向量a×向量b=-向量b×向量a在物理学中，已知力与力臂求外积，就是向量的外积，即叉乘。

      向量c的方向与a,b所在的平面垂直，且方向要用“右手法则”判断。判断方法如下：

    1.右手手掌张开，四指并拢，大拇指垂直于四指指向的方向；
    2.伸出右手，四指弯曲，四指与A旋转到B方向一致，那么大拇指指向为C向量的方向。
                                        

      另外还有一个有用的特征那就是叉积的绝对值就是A和B为两边说形成的平行四边形的面积。也就是AB所包围三角形面积的两倍。在计算面积时，我们要经常用到叉积。
     * 
     * 二维向量叉乘：(x1,y1)×(x2,y2) = x1y2-x2y1
        值的绝对值是两向量同起点，构成平行四边形的面积
        值为正，(x2,y2)在(x1,y1)逆时针方向
        值为负，(x2,y2)在(x1,y1)顺时针方向
        值为0，(x2,y2)和(x1,y1)共线
     * @param {number} a
     * @param {number} b
     * @return {*}
     */
    public static cross(a: number[], b: number[]): number[] {
        let x1 = a[0];
        let y1 = a[1];
        let z1 = a[2];
        let x2 = b[0];
        let y2 = b[1];
        let z2 = b[2];
        return [y1 * z2 - y2 * z1,x2 * z1 - x1 * z2,x1 * y2 - x2 * y1];
    }

    /**
     * @Author: Snow
     * @description: 点乘
     * 如果说加法是凭直觉就可以知道的，另外还有一些运算就不是那么明显的，比如点乘和叉乘。
      点乘比较简单，是相应元素的乘积的和：
      V1( x1, y1)   V2(x2, y2) = x1*x2 + y1*y2
      注意结果不是一个向量，而是一个标量（Scalar）。点乘有什么用呢，我们有：
      A   B = |A||B|Cos(θ)
      θ是向量A和向量B见的夹角。这里|A|我们称为向量A的模(norm)，也就是A的长度， 在二维空间中就是|A| = sqrt(x2+y2)。这样我们就和容易计算两条线的夹角：    Cos(θ) = AB /(|A||B|)

      当然你知道要用一下反余弦函数acos()啦。（回忆一下cos(90)=0 和cos(0) = 1还是有好处的，希望你没有忘记。）这可以告诉我们如果点乘的结果，简称点积，为0的话就表示这两个向量垂直。当两向量平行时，点积有最大值
      另外，点乘运算不仅限于2维空间，他可以推广到任意维空间。（译注：不少人对量子力学中的高维空间无法理解，其实如果你不要试图在视觉上想象高维空间，而仅仅把它看成三维空间在数学上的推广，那么就好理解了）
     * @param {number} a
     * @param {number} b
     * @return {*}
     */
    public static dot(a: number[], b: number[]): number {
        return a[0] * b[0] + a[1] * b[1]  + a[2] * b[2];
    }

    /**
     * @Author: Snow
     * @description: 向量长度
     * @param {number} a
     * @return {*}
     */    
     public static len(a:number[]):number {
        let x = a[0];
        let y = a[1];
        let z = a[2];
        return Math.sqrt(x * x + y * y+ z * z);
    }

    /**
     * @Author: Snow
     * @description: 求逆
     * @param {number} a
     * @return {*}
     */    
    public static inverse(a:number[]):number[] {
        let out:number[] = [];
        out[0] = 1.0 / a[0];
        out[1] = 1.0 / a[1];
        out[2] = 1.0 / a[2];
        return out;
    }

    /**
     * @Author: Snow
     * @description: 单位向量
     * @param {number} a
     * @return {*}
     */    
    public static normalize(a:number[]):number[] {
        let out:number[] = [];
        let x = a[0];
        let y = a[1];
        let z = a[2];
        var len = x * x + y * y + z*z;
        if (len > 0) {
            // TODO: evaluate use of glm_invsqrt here?
            len = 1 / Math.sqrt(len);
            out[0] = a[0] * len;
            out[1] = a[1] * len;
            out[2] = a[2] * len;
        }else{
            out[0] = a[0];
            out[1] = a[1];
            out[2] = a[2];
        }
        return out;
    }
}